﻿using System.Reflection;
using System.Security.Permissions;
using System.Collections;
using System.Threading;
using System.Security.Principal;
using System.Web.Hosting;
using System.Net;
namespace System.Web
{
    public partial class NetHttpApplication : INetHttpAsyncHandler, INetHttpHandler, IDisposable
    {
        private NetHttpApplicationFactory _factory;
        private NetHttpModuleCollection _moduleCollection;
        private NetHttpContext _initContext;
        private NetHttpContext _context;
        private NetHttpApplicationState _state;
        private HttpSessionStateBase _session;
        private Exception _lastError;
        private bool _hideRequestResponse;
        private bool _timeoutManagerInitialized;

        public void Dispose() { }

        #region Init

        private string _currentModuleCollectionKey;
        private bool _initInternalCompleted;

        internal void InitInternal(NetHttpWorkerRequest wr, NetHttpContext context, NetHttpApplicationState state, NetHttpApplicationFactory factory, MethodInfo[] handlers)
        {
            _state = state;
            _factory = factory;
            try
            {
                _initContext = context;
                _initContext.NetApplicationInstance = this;
                InitModules(wr);
                if (handlers != null)
                    HookupEventHandlersForApplicationAndModules(handlers);
                _context = context;
                if (_context != null)
                    _context.HideRequestResponse = true;
                _hideRequestResponse = true;
                try { Init(); }
                catch (Exception exception) { RecordError(exception); }
                if (_context != null)
                    _context.HideRequestResponse = false;
                _hideRequestResponse = false;
                _context = null;
                _resumeStepsWaitCallback = new WaitCallback(ResumeStepsWaitCallback);
                _stepManager = new ApplicationNetStepManager(this);
                _stepManager.BuildSteps(_resumeStepsWaitCallback);
            }
            finally
            {
                _initInternalCompleted = true;
                _initContext.NetApplicationInstance = null;
                _initContext = null;
            }
        }

        private void InitModules(NetHttpWorkerRequest wr)
        {
            var wrAsWebHost = (wr as WebHostWorkerRequest);
            if (wrAsWebHost == null)
                throw new InvalidOperationException();
            var webHostRuntime = wrAsWebHost.WebHostRuntime;
            if (webHostRuntime == null)
                throw new InvalidOperationException();
            _moduleCollection = webHostRuntime.Modules;
            //
            int count = _moduleCollection.Count;
            for (int index = 0; index < count; index++)
            {
                _currentModuleCollectionKey = _moduleCollection.GetKey(index);
                _moduleCollection[index].Init(this);
            }
            _currentModuleCollectionKey = null;
            InitAppLevelCulture();
        }

        #endregion

        #region Properties

        public NetHttpModuleCollection Modules
        {
            get
            {
                if (_moduleCollection == null)
                    _moduleCollection = new NetHttpModuleCollection();
                return _moduleCollection;
            }
        }

        public HttpRequestBase Request
        {
            get
            {
                HttpRequestBase request = null;
                if ((_context != null) && !_hideRequestResponse)
                    request = _context.Request;
                if (request == null)
                    throw new HttpException("Request_not_available");
                return request;
            }
        }

        public HttpResponseBase Response
        {
            get
            {
                HttpResponseBase response = null;
                if ((_context != null) && !_hideRequestResponse)
                    response = _context.Response;
                if (response == null)
                    throw new HttpException("Response_not_available");
                return response;
            }
        }

        //public HttpServerUtilityBase Server
        //{
        //    get
        //    {
        //        if (_context != null)
        //            return _context.Server;
        //        return new NetHttpServerUtility(this);
        //    }
        //}

        public HttpSessionStateBase Session
        {
            get
            {
                HttpSessionStateBase session = null;
                if (_session != null)
                    session = _session;
                else if (_context != null)
                    session = _context.Session;
                if (session == null)
                    throw new HttpException("Session_not_available");
                return session;
            }
        }

        //public ISite Site
        //{
        //    get { return _site; }
        //    set { _site = value; }
        //}

        public IPrincipal User
        {
            get
            {
                if (_context == null)
                    throw new HttpException("User_not_available");
                return _context.User;
            }
        }

        #endregion

        #region Special

        private bool _initSpecialCompleted;

        internal void InitSpecial(NetHttpContext context, NetHttpApplicationState state, NetHttpApplicationFactory factory, MethodInfo[] handlers)
        {
            _state = state;
            _factory = factory;
            try
            {
                if (context != null)
                {
                    _initContext = context;
                    _initContext.NetApplicationInstance = this;
                }
                InitAppLevelCulture();
                if (handlers != null)
                    HookupEventHandlersForApplicationAndModules(handlers);

            }
            finally
            {
                _initSpecialCompleted = true;
                if (_initContext != null)
                {
                    _initContext.NetApplicationInstance = null;
                    _initContext = null;
                }
            }
        }

        internal void ProcessSpecialRequest(NetHttpContext context, MethodInfo method, int paramCount, object eventSource, EventArgs eventArgs, HttpSessionStateBase session)
        {
            _context = context;
            if (_context != null)
                _context.HideRequestResponse = true;
            _hideRequestResponse = true;
            _session = session;
            _lastError = null;
            try
            {
                SetAppLevelCulture();
                InvokeMethodWithAssert(method, paramCount, eventSource, eventArgs);
            }
            catch (Exception exception)
            {
                var innerException = (exception is TargetInvocationException ? exception.InnerException : exception);
                RecordError(innerException);
            }
            finally
            {
                if (_state != null)
                    _state.EnsureUnLock();
                RestoreAppLevelCulture();
                if (_context != null)
                    _context.HideRequestResponse = false;
                _hideRequestResponse = false;
                _context = null;
                _session = null;
                _lastError = null;
                _appEvent = null;
            }
        }

        [ReflectionPermission(SecurityAction.Assert, Flags = ReflectionPermissionFlag.RestrictedMemberAccess)]
        private void InvokeMethodWithAssert(MethodInfo method, int paramCount, object eventSource, EventArgs eventArgs)
        {
            if (paramCount == 0)
                method.Invoke(this, new object[0]);
            else
                method.Invoke(this, new object[] { eventSource, eventArgs });
        }

        #endregion

        public virtual void Init() { }

        internal void RecordError(Exception error) { }

        internal void CompleteRequest() { }

        internal INetHttpHandler MapHttpHandler(NetHttpContext context, string p, string p_2, string p_3, bool p_4)
        {
            return null;
        }

        public NetHttpApplicationState Application
        {
            get { return _state; }
        }

        internal NetHttpApplicationFactory ApplicationFactory
        {
            get { return _factory; }
        }

        public NetHttpContext Context
        {
            get { return (_initContext ?? _context); }
        }


        bool IHttpHandler.IsReusable
        {
            get { return true; }
        }

        void IHttpHandler.ProcessRequest(HttpContext context) { }

        void INetHttpHandler.ProcessRequest(HttpContextBase context)
        {
            throw new HttpException("Sync_not_supported");
        }

        IAsyncResult INetHttpAsyncHandler.BeginProcessRequest(NetHttpContext context, AsyncCallback cb, object extraData)
        {
            _context = context;
            _context.NetApplicationInstance = this;
            _stepManager.InitRequest();
            var result = new NetHttpAsyncResult(cb, extraData);
            AsyncResult = result;
            ResumeSteps(null);
            return result;
        }

        void INetHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
        {
            var result2 = (NetHttpAsyncResult)result;
            if (result2.Error != null)
                throw result2.Error;
        }

        internal ThreadContext OnThreadEnter() { return OnThreadEnter(true); }
        internal ThreadContext OnThreadEnter(bool setImpersonationContext)
        {
            var context = new ThreadContext(_context);
            context.Enter(setImpersonationContext);
            if (!_timeoutManagerInitialized)
            {
                _context.WorkerRequest.EnsureTimeout();
                HttpListenerHost.RequestTimeoutManager.Add(_context.WorkerRequest);
                _timeoutManagerInitialized = true;
            }
            return context;
        }

        internal void ReleaseAppInstance()
        {
            if (_context != null)
            {
                _context.ClearReferences();
                if (_timeoutManagerInitialized)
                {
                    HttpListenerHost.RequestTimeoutManager.Remove(_context.WorkerRequest);
                    _timeoutManagerInitialized = false;
                }
            }
            RecycleHandlers();
            if (AsyncResult != null)
                AsyncResult = null;
            _context = null;
            AppEvent = null;
            ApplicationFactory.RecycleApplicationInstance(this);
        }

        private void RecycleHandlers()
        {
        }
    }
}
